#include "scene.h"

MaterialLib Scene::matlib;

Scene::Scene()
{
  maze_size_ = 10;
  maze_ = new Maze(maze_size_, maze_size_);
  player_model_ = NULL;
  enemy_model_ = NULL;
  player_ = NULL;
  enemy_count_ = 3;
  enemies_ = std::vector<Character*>(enemy_count_);
  for (unsigned int i = 0; i < enemies_.size(); i++) {
    enemies_[i] = NULL;
  }
  bullets_ = std::vector<Bullet*>(kNumBullets);
  for (unsigned int i = 0; i < bullets_.size(); i++) {
    bullets_[i] = NULL;
  }
  bullet_count_ = 0;
  destruction_enabled_ = false;
}

Scene::~Scene()
{
  RemovePlayer();
  RemoveEnemies();
  RemoveBullets();
  delete enemy_model_;
  delete player_model_;
  delete maze_;
}

void Scene::Reset()
{
  ResetPlayer();
  ResetEnemies();
  RemoveBullets();
}

void Scene::RemovePlayer()
{
  if (player_ != NULL) {
    delete player_;
    player_ = NULL;
  }
}

void Scene::RemoveEnemy(int i)
{
  if (i >= 0 && i < (int)enemies_.size() && enemies_[i] != NULL) {
    int x = enemies_[i]->GetX();
    int z = enemies_[i]->GetZ();
    maze_->SetOccupied(x, z, false);
    delete enemies_[i];
    enemies_[i] = NULL;
  }
}

void Scene::RemoveEnemies()
{
  for (unsigned int i = 0; i < enemies_.size(); i++) {
    if (enemies_[i] != NULL) {
      // First free the maze position occupied by the enemy
      int x = enemies_[i]->GetX();
      int z = enemies_[i]->GetZ();
      maze_->SetOccupied(x, z, false);
      delete enemies_[i];
      enemies_[i] = NULL;
    }
  }
}

void Scene::RemoveBullet(int i)
{
  if ((unsigned int)i < bullets_.size()) {
    delete bullets_[i];
    bullets_[i] = NULL;
    bullet_count_--;
  }
}

void Scene::RemoveBullets()
{
  for (unsigned int i = 0; i < bullets_.size(); i++) {
    if (bullets_[i] != NULL) {
      delete bullets_[i];
      bullets_[i] = NULL;
      bullet_count_--;
    }
  }
}

void Scene::ResetPlayer()
{
  RemovePlayer();
  Point pos = maze_->GetCenter();
  int x = (int)pos.x;
  int z = (int)pos.z;
  player_ = new Character(x, z, *player_model_);
  // If the center is not ground we switch the type so that the player can move
  if (!maze_->IsGround(x, z)) {
    maze_->Switch(x, z);
  }
  // If there is an enemy in the player position we have no choice but to reset the enemies
  if (maze_->IsOccupied(x, z)) {
    ResetEnemies();
  }
}

void Scene::ResetEnemies()
{
  RemoveEnemies();
  enemies_ = std::vector<Character*>(enemy_count_);
  int n = enemies_.size();
  int i = 0;
  int j = 0;
  // Check the maze borders to see if there are any positions for the enemies to occupy.
  while (n > 0 &&  j < maze_size_) {
    if (i == 0 || i == maze_size_ - 1 || j == 0 || j == maze_size_ - 1) {
      if (maze_->SetOccupied(i, j, true)) {
        enemies_[n - 1] = new Character(i, j, *enemy_model_);
        n--;
      }
    }
    i += 1;
    if (i >= maze_size_) {
      i = 0;
      j += 1;
    }
  }
  // If not enough spaces are found, don't put all the enemies and ignore the missing ones
  while (n > 0) {
    enemies_[n - 1] = NULL;
    n--;
  }
}

void Scene::GenerateMaze(int size)
{
  RemoveEnemies();
  RemovePlayer();
  RemoveBullets();
  delete maze_;
  maze_size_ = size;
  maze_ = new Maze(size, size);
}

void Scene::Click(int x, int z)
{
  if (player_ != NULL) {
    int i = player_->GetX();
    int j = player_->GetZ();
    if (x != i || z != j) {
      maze_->Switch(x, z);
    }
  }
  else {
    maze_->Switch(x, z);
  }
}

Point Scene::GetCenter()
{
  return maze_->GetCenter();
}

float Scene::GetRadius()
{
  return maze_->GetRadius();
}

void Scene::LoadPlayerModel(QString file)
{
  if (player_model_ == NULL) {
    player_model_ = new Model("Player");
  }
  player_model_->readObj(file.toAscii(), matlib);
  // this loaded the same model for the enemies if they didn't have a model
//  if (enemy_model_ == NULL) {
//    enemy_model_ = new Model("Enemy");
//    enemy_model_->readObj(file.toAscii(), matlib);
//  }
  ResetPlayer();
  // This loaded the enemies at the same time as the player so you didn't have to do it twice to play
//  ResetEnemies();
  RemoveBullets();
}

void Scene::MovePlayer(Movement mov)
{
  if (player_ != NULL) {
    player_->Move(mov);
    int x = player_->GetX();
    int z = player_->GetZ();
    if (!maze_->IsGround(x, z)) {
      player_->Undo(mov);
    }
    //TODO: comprovar colisions amb altres pjs ?
    // Ara ja es mira si els enemics et trepitjan
  }
}

Point Scene::GetPlayerPosition()
{
  Point position;
  if (player_ != NULL) {
    position = player_->GetPosition();
  }
  else {
    position = GetCenter();
    position = Point((int)position.x + 0.5, kGroundHeight, (int)position.z + 0.5);
  }
  position.y = kWallHeight * 0.8;
  return position;
}

Direction Scene::GetPlayerOrientation()
{
  if (player_ != NULL) {
    return player_->GetOrientation();
  }
  else {
    return NORTH;
  }
}

void Scene::SetEnemyCount(int n)
{
  enemy_count_ = n;
}

void Scene::LoadEnemyModel(QString file)
{
  if (enemy_model_ == NULL) {
    enemy_model_ = new Model("Enemy");
  }
  enemy_model_->readObj(file.toAscii(), matlib);
  ResetEnemies();
}

void Scene::Update()
{
  time_ = (time_ + 1) % 4;

  // Update bullets
  Bullet *b;
  for (unsigned int i = 0; i < bullets_.size(); i++) {
    b = bullets_[i];
    if (b != NULL) {
      b->Update();
      if (time_ == 0) {
        int bx = b->GetX();
        int bz = b->GetZ();
        if (!maze_->IsGround(bx, bz)) {
          RemoveBullet(i);
          if (destruction_enabled_) maze_->Shoot(bx, bz);
        }
        if (maze_->IsOccupied(bx, bz)) {
          RemoveBullet(i);
          for (unsigned int i = 0; i < enemies_.size(); i++) {
            if (enemies_[i] != NULL) {
              int ex = enemies_[i]->GetX();
              int ez = enemies_[i]->GetZ();
              if (pcmp(bx, bz, ex, ez)) {
                RemoveEnemy(i);
              }
            }
          }
        }
      }
    }
  }

  // Update enemies
  if (time_ == 0 && player_ != NULL) {
    // Els enemics nomes s'actualitzen cada 4 frames, ja que van 4 cops mes lents que els cubs
    // Si no hi ha jugador el joc esta aturat
    bool game_finished = false;
    for (unsigned int i = 0; i < enemies_.size(); i++) {
      if (enemies_[i] != NULL) {
        int times = 4;
        while (times > 0) {
          Movement mov = rand()%6!=0 ? FRONT : rand()%2!=0 ? LEFT : RIGHT;
          int ox = enemies_[i]->GetX();
          int oz = enemies_[i]->GetZ();
          enemies_[i]->Move(mov);
          int nx = enemies_[i]->GetX();
          int nz = enemies_[i]->GetZ();

          // Si la posicio no es terra el moviment no es valid
          if (!maze_->IsGround(nx, nz)) {
            enemies_[i]->Undo(mov);
          }
          // Si ha canviat de lloc i la posicio nova esta ocupada
          else if (!pcmp(ox, oz, nx, nz) && maze_->IsOccupied(nx, nz)) {
            enemies_[i]->Undo(mov);
          }
          // Altrement es un moviment valid
          else {
            maze_->SetOccupied(ox, oz, false);
            maze_->SetOccupied(nx, nz, true);
            times = 0;
          }
          // Check if the enemy has crushed the player
          int px = player_->GetX();
          int pz = player_->GetZ();
          if (pcmp(px, pz, nx, nz)) {
            game_finished = true;
          }
          times--;
        }
      }
    }
    if (game_finished) {
      RemovePlayer();
      RemoveEnemies();
      RemoveBullets();
    }
  }
}

bool Scene::IsReady()
{
  if (player_ == NULL || player_model_ == NULL || enemy_model_ == NULL) {
    return false;
  }
  for (unsigned int i = 0; i < enemies_.size(); i++) {
    if (enemies_[i] != NULL) return true;
  }
  return false;
}

void Scene::Render(bool render_player, int player_rotation)
{
  // Aqui s'ha de pintar l'escena
  maze_->Render();
  if (player_ != NULL && render_player) {
    player_->Render(player_rotation);
  }
  for (unsigned int i = 0; i < enemies_.size(); i++) {
    if (enemies_[i] != NULL) {
      enemies_[i]->Render();
    }
  }
  for (unsigned int i = 0; i < bullets_.size(); i++) {
    if (bullets_[i] != NULL) {
      bullets_[i]->Render();
    }
  }
}

void Scene::PickRender()
{
  maze_->PickRender();
}

void Scene::EnemyPickRender()
{
  for (GLubyte i = 0; i < enemies_.size(); i++) {
    if (enemies_[i] != NULL) {
      GLubyte color[3] = {i, 0, 0};
      enemies_[i]->PickRender(color);
    }
  }
}

void Scene::Shoot()
{
  std::cout << bullet_count_ << std::endl;
  if (player_ != NULL && (unsigned int)bullet_count_ < kNumBullets) {
    for (unsigned int i = 0; i < bullets_.size(); i++) {
      if (bullets_[i] == NULL) {
        bullets_[i] = new Bullet(player_->GetPosition(), player_->GetOrientation());
        bullet_count_++;
        return;
      }
    }
  }
}

void Scene::SwitchDestruction()
{
  destruction_enabled_ = !destruction_enabled_;
}

bool Scene::IsPlayerAlive()
{
  return player_ != NULL;
}
